# encoding:utf-8
import json
import traceback
import logging

from redis.exceptions import RedisError
from sqlalchemy.orm import load_only

from common.models import rds
from common.models.information import User
from common.utils.cache_util import UserProfileCacheTTL


class UserCache(object):
    """
    用户信息缓存相关
    """

    def __init__(self, user_id):
        self.key = 'user:{}:profile'.format(user_id)
        self.user_id = user_id

    def get(self):
        """
        从Redis中获取用户缓存信息
        """
        # 思路:
        # 查询Redis中的缓存记录
        # 如果记录存在, 则直接返回
        # 如果记录不存在
        #     去数据库中查询, 如果数据库中存在, 则设置Redis缓存记录, 并返回查询数据
        #     数据库中如果没有记录, 设置Redis中记录的值为-1
        # 返回值
        try:
            ret = rds.get(self.key)
        except RedisError as e:
            logging.error('写入日志: {}'.format(e))
            ret = None
        if ret is not None:
            if ret == b'1':
                return None
            else:
                user_dict = json.loads(ret)
                return user_dict
        else:
            try:
                user = self._get_user_obj()
            except DatabaseError as e:
                logging.error('写入日志: {}'.format(e))
                raise e
            if user is None:
                rds.setex(self.key, UserNotExistsCacheTTL.get_val(), -1)
                return None
            else:
                birthday = user.birthday.strftime('%Y-%m-%d') if user.birthday else None
                user_dict = self._get_user_dict()
                try:
                    rds.setex(self.key, UserProfileCacheTTL.get_val(), json.dumps(user_dict))
                except RedisError as e:
                    logging.error('写入日志: {}'.format(e))

            return user_dict

    def clear(self):
        """
        清除缓存记录
        :return:
        """
        try:
            rds.delete(self.key)
        except RedisError as e:
            logging.error('Cache clear is error:{}'.format(e))

    def determine_user_exists(self):
        try:
            ret = rds.get(self.key)
        except RedisError as e:
            # 记录日志
            logging.error('Cache determine_user_exists is error:{}'.format(e))
            # 在redis出现异常的时候，为了保证我们封装的get方法还能有返回值，可以进入数据库查询的部分
            ret = None
        if ret is not None:
            # 表示redis中有记录值
            # 判断redis记录是表示数据库不存在的-1值还是有意义的缓存记录
            # 切记： python3 从redis中取出的字符串数据是python中的bytes
            if ret == b'-1':
                return False
            else:
                return True
        else:
            try:
                user = self._get_user_obj()
            except DatabaseError as e:
                logging.error('写入日志: {}'.format(e))
                # 对于这个数据库异常，我们自己封装的get方法无法为调用者做决定，决定返回什么值，所以抛出异常给调用者，由调用者决定
                raise e

        # 在sqlalchemy中，查询单一对象，数据库不存在，不抛出异常，只返回None
        if user is None:
            # 数据库不存在
            try:
                r.setex(self.key, UserNotExistsCacheTTL.get_val(), -1)
            except RedisError as e:
                logging.error('写入日志: {}'.format(e))
                return False
        else:
            birthday = user.birthday.strftime('%Y-%m-%d') if user.birthday else None
            user_dict = self._get_user_dict()
            try:
                r.setex(self.key, UserProfileCacheTTL.get_val(), json.dumps(user_dict))
            except RedisError as e:
                logging.error('写入日志: {}'.format(e))
        return True

    def _get_user_obj(self):
        user = User.query.options(load_only(
            User.mobile,
            User.user_name,
            User.gender,
            User.birthday,
            User.profile_photo,
            User.is_media,
            User.introduction,
            User.certificate,
            User.article_count,
            User.following_count,
            User.fans_count,
            User.like_count
        )).filter_by(uid=self.user_id).first()
        return user

    def _get_user_dict(self):
        user_dict = {
            'mobile': User.mobile,
            'user_name': User.user_name,
            'gender': User.gender,
            'birthday': User.birthday.strftime('%Y-%m-%d'),
            'profile_photo': User.profile_photo,
            'is_media': User.is_media,
            'introduction': User.introduction,
            'certificate': User.certificate,
            'article_count': User.article_count,
            'following_count': User.following_count,
            'fans_count': User.fans_count,
            'like_count': User.like_count
        }
        return user_dict
